#pragma once
#include <zCoreConfig.hpp>
#include <common.hpp>
#include "Singleton.hpp"
#include "zVar.hpp"
#include "StringTools.hpp"

namespace zzz{
/////////////////////////////////////////////////
// VerboseLevel Setter
class VerboseLevel
{
public:
  static int Set(int x) {
    old_=Get();
    verbose_level_=x;
    return old_;
  }
  static void Restore() {
    Set(old_);
  }
  static int Get() {
    return verbose_level_;
  }
private:
  ZCORE_FUNC static int old_;
  ZCORE_FUNC static int verbose_level_;
};

class ZCORE_CLASS GlobalLogger : public Singleton<GlobalLogger>
{
public:

  GlobalLogger();
  ~GlobalLogger();

  void Clear();
  void Add(ostream *s);
  typedef enum { WHITE, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, GRAY, DARKRED, DARKGREEN, DARKYELLOW, DARKBLUE, DARKMAGENTA, DARKCYAN, } LoggerColor;
  void SetColor(LoggerColor color);
  void TestPrint();

  ZCORE_FUNC friend GlobalLogger& operator<<(GlobalLogger &,basic_ostream<char>& (&func)(basic_ostream<char>& _Ostr));
  ZCORE_FUNC friend GlobalLogger& operator<<(GlobalLogger &,ostream &v);
  ZCORE_FUNC friend GlobalLogger& operator<<(GlobalLogger &,const string &v);
  ZCORE_FUNC friend GlobalLogger& operator<<(GlobalLogger &,const char *v);
  
private:
  vector<ostream*> ostreams_;
  vector<ofstream*> ofstreams_;
  void ReallySetColor();
  LoggerColor next_color_;
  LoggerColor cur_color_;
};

ZCORE_FUNC GlobalLogger& operator<<(GlobalLogger &,basic_ostream<char>& (&func)(basic_ostream<char>& _Ostr));
ZCORE_FUNC GlobalLogger& operator<<(GlobalLogger &,ostream &v);
ZCORE_FUNC GlobalLogger& operator<<(GlobalLogger &,const string &v);
ZCORE_FUNC GlobalLogger& operator<<(GlobalLogger &,const char *v);

template<typename T>
GlobalLogger& operator<<(GlobalLogger &g, const T &v)
{
  return g<<(ToString(v));
}

////////////////////////////////////////////
#ifdef ZZZ_NO_LOG
#define zout (cout)
#else
#define zout (GlobalLogger::Instance())
#endif

/////////////////////////////////////////////
// Predefined Verbose Level
// All debug information, generally the information is useless.
const int ZDEBUG=0;
// Less important information, report more detailed message.
const int ZVERBOSE=30;
// Normal information.
const int ZINFO=60;
// More important information, highlighting.
const int ZMORE=70;
// An error occurred, but it can be handled.
const int ZERROR=80;
// An unhandlable error occurred, cannot continue.
const int ZFATAL=100;

////////////////////////////////////////////
class ZCORE_CLASS LevelLogger {
public:
  explicit LevelLogger(int level, const char *filename=NULL, int line=-1):level_(level) {
    if (level_ >= ZFATAL) zout.SetColor(GlobalLogger::MAGENTA);
    else if (level_ >= ZERROR) zout.SetColor(GlobalLogger::RED);
    else if (level_ >= ZMORE) zout.SetColor(GlobalLogger::YELLOW);
    else if (level_ >= ZINFO) zout.SetColor(GlobalLogger::WHITE);
    else if (level_ >= ZVERBOSE) zout.SetColor(GlobalLogger::GRAY);
    else zout.SetColor(GlobalLogger::GREEN);
    if (filename!=NULL && level >= VerboseLevel::Get()) {
      // Visual Studio Style
      zout<<filename<<'('<<line<<"): ";
    }

  }
  ~LevelLogger();

  ZCORE_FUNC friend const LevelLogger& operator<<(const LevelLogger &,basic_ostream<char>& (&func)(basic_ostream<char>& _Ostr));
  ZCORE_FUNC friend const LevelLogger& operator<<(const LevelLogger &,ostream &v);
  ZCORE_FUNC friend const LevelLogger& operator<<(const LevelLogger &,const string &v);
  ZCORE_FUNC friend const LevelLogger& operator<<(const LevelLogger &,const char *v);

private:
  int level_;
};

ZCORE_FUNC const LevelLogger& operator<<(const LevelLogger &,basic_ostream<char>& (&func)(basic_ostream<char>& _Ostr));
ZCORE_FUNC const LevelLogger& operator<<(const LevelLogger &,ostream &v);
ZCORE_FUNC const LevelLogger& operator<<(const LevelLogger &,const string &v);
ZCORE_FUNC const LevelLogger& operator<<(const LevelLogger &,const char *v);

template<typename T>
const LevelLogger& operator<<(const LevelLogger &logger, const T &v) {
  ostringstream oss;
  oss<<v;
  return logger<<oss.str();
}

/////////////////////////////////////////////
class DummyLevelLogger : public LevelLogger
{
public:
  DummyLevelLogger():LevelLogger(-1){};
  friend const DummyLevelLogger& operator<<(const DummyLevelLogger &,basic_ostream<char>& (&func)(basic_ostream<char>& _Ostr));
  friend const DummyLevelLogger& operator<<(const DummyLevelLogger &,ostream &v);
  friend const DummyLevelLogger& operator<<(const DummyLevelLogger &,const string &v);
  friend const DummyLevelLogger& operator<<(const DummyLevelLogger &,const char *v);
  template<typename T>
  friend const DummyLevelLogger& operator<<(const DummyLevelLogger &, const T &v);
};
template<typename T>
const DummyLevelLogger& operator<<(const DummyLevelLogger &logger, const T &v)
{
  return ;
}

// Normal log, always logging
// ZL* will output the file and line of logging place
#define ZLOG(level) if (level >= VerboseLevel::Get()) LevelLogger(level)
#define ZLLOG(level) if (level >= VerboseLevel::Get()) LevelLogger(level,__FILE__,__LINE__)

// Debug log, only log at debug mode
#ifdef ZZZ_DEBUG
#define ZDLOG(level) ZLOG(level)
#define ZLDLOG(level) ZLLOG(level)
#else
#define ZDLOG(level) (DummyLevelLogger())
#define ZLDLOG(level) (DummyLevelLogger())
#endif

// Release log, only log at non-fastest mode
#ifndef ZZZ_FASTEST
#define ZRLOG(level) ZLOG(level)
#define ZLRLOG(level) ZLLOG(level)
#else
#define ZRLOG(level) (DummyLevelLogger())
#define ZLRLOG(level) (DummyLevelLogger())
#endif

#define ZCHECK_WITH_MSG(x, msg) if (!(x)) ZLLOG(ZFATAL)<<msg

#ifdef ZZZ_DEBUG
#define ZDCHECK_WITH_MSG(x, msg) if (!(x)) ZLDLOG(ZFATAL)<<msg
#else
#define ZDCHECK_WITH_MSG(x, msg) (x);DummyLevelLogger()
#endif

#ifndef ZZZ_FASTEST
#define ZRCHECK_WITH_MSG(x, msg) if (!(x)) ZLRLOG(ZFATAL)<<msg
#else
#define ZRCHECK_WITH_MSG(x, msg) (x);DummyLevelLogger()
#endif


#define ZCHECK(x) ZCHECK_WITH_MSG((x), #x<<" must be TRUE!")
#define ZCHECK_FALSE(x) ZCHECK_WITH_MSG((!(x)), #x<<" must be FALSE!")

#define ZDCHECK(x) ZDCHECK_WITH_MSG((x), #x<<" is false!")
#define ZDCHECK_FALSE(x) ZDCHECK_WITH_MSG((!(x)), #x<<" must be FALSE!")

#define ZRCHECK(x) ZRCHECK_WITH_MSG((x), #x<<" is false!")
#define ZRCHECK_FALSE(x) ZRCHECK_WITH_MSG((!(x)), #x<<" must be FALSE!")

// Combine
#define ZCHECK_BOTH(x, y) ZCHECK_WITH_MSG((x) && (y), "Both "<< #x <<" and "<<#y<<" must be TRUE!")
#define ZCHECK_EITHER(x, y) ZCHECK_WITH_MSG((x) || (y), "Either "<<#x <<" or "<<#y<<" must be TRUE!")
#define ZCHECK_NEITHER(x, y) ZCHECK_WITH_MSG(!(x) && !(y), "Neither "<<#x <<" or "<<#y<<" can be TRUE!")

#define ZDCHECK_BOTH(x, y) ZDCHECK_WITH_MSG((x) && (y), "Both "<< #x <<" and "<<#y<<" must be TRUE!")
#define ZDCHECK_EITHER(x, y) ZDCHECK_WITH_MSG((x) || (y), "Either "<<#x <<" or "<<#y<<" must be TRUE!")
#define ZDCHECK_NEITHER(x, y) ZDCHECK_WITH_MSG(!(x) && !(y), "Neither "<<#x <<" or "<<#y<<" can be TRUE!")

// Math
#define ZCHECK_EQ(x, y) ZCHECK_WITH_MSG((x)==(y), #x<<" must be equal to "<<#y<<ZVAR(x)<<ZVAR(y))
#define ZCHECK_NE(x, y) ZCHECK_WITH_MSG((x)!=(y), #x<<" must be not equal to "<<#y<<ZVAR(x)<<ZVAR(y))
#define ZCHECK_LT(x, y) ZCHECK_WITH_MSG((x)<(y), #x<<" must be less than "<<#y<<ZVAR(x)<<ZVAR(y))
#define ZCHECK_LE(x, y) ZCHECK_WITH_MSG((x)<=(y), #x<<" must be no more than "<<#y<<ZVAR(x)<<ZVAR(y))
#define ZCHECK_GT(x, y) ZCHECK_WITH_MSG((x)>(y), #x<<" must be greater than "<<#y<<ZVAR(x)<<ZVAR(y))
#define ZCHECK_GE(x, y) ZCHECK_WITH_MSG((x)>=(y), #x<<" must be no less than "<<#y<<ZVAR(x)<<ZVAR(y))

#define ZDCHECK_EQ(x, y) ZDCHECK_WITH_MSG((x)==(y), #x<<" must be equal to "<<#y<<ZVAR(x)<<ZVAR(y))
#define ZDCHECK_NE(x, y) ZDCHECK_WITH_MSG((x)!=(y), #x<<" must be not equal to "<<#y<<ZVAR(x)<<ZVAR(y))
#define ZDCHECK_LT(x, y) ZDCHECK_WITH_MSG((x)<(y), #x<<" must be less than "<<#y<<ZVAR(x)<<ZVAR(y))
#define ZDCHECK_LE(x, y) ZDCHECK_WITH_MSG((x)<=(y), #x<<" must be no more than "<<#y<<ZVAR(x)<<ZVAR(y))
#define ZDCHECK_GT(x, y) ZDCHECK_WITH_MSG((x)>(y), #x<<" must be greater than "<<#y<<ZVAR(x)<<ZVAR(y))
#define ZDCHECK_GE(x, y) ZDCHECK_WITH_MSG((x)>=(y), #x<<" must be no less than "<<#y<<ZVAR(x)<<ZVAR(y))

#define ZRCHECK_EQ(x, y) ZRCHECK_WITH_MSG((x)==(y), #x<<" must be equal to "<<#y<<ZVAR(x)<<ZVAR(y))
#define ZRCHECK_NE(x, y) ZRCHECK_WITH_MSG((x)!=(y), #x<<" must be not equal to "<<#y<<ZVAR(x)<<ZVAR(y))
#define ZRCHECK_LT(x, y) ZRCHECK_WITH_MSG((x)<(y), #x<<" must be less than "<<#y<<ZVAR(x)<<ZVAR(y))
#define ZRCHECK_LE(x, y) ZRCHECK_WITH_MSG((x)<=(y), #x<<" must be no more than "<<#y<<ZVAR(x)<<ZVAR(y))
#define ZRCHECK_GT(x, y) ZRCHECK_WITH_MSG((x)>(y), #x<<" must be greater than "<<#y<<ZVAR(x)<<ZVAR(y))
#define ZRCHECK_GE(x, y) ZRCHECK_WITH_MSG((x)>=(y), #x<<" must be no less than "<<#y<<ZVAR(x)<<ZVAR(y))

// Within
#define ZCHECK_WITHIN(_min, _x, _max) ZCHECK_WITH_MSG(Within(_min, _x, _max), #_x<<" must be within "<<_min<<" and "<<_max<<ZVAR(_x))
#define ZDCHECK_WITHIN(_min, _x, _max) ZDCHECK_WITH_MSG(Within(_min, _x, _max), #_x<<" must be within "<<_min<<" and "<<_max<<ZVAR(_x))
#define ZRCHECK_WITHIN(_min, _x, _max) ZRCHECK_WITH_MSG(Within(_min, _x, _max), #_x<<" must be within "<<_min<<" and "<<_max<<ZVAR(_x))

// NULL positive, negative
#define ZCHECK_NULL(x) ZCHECK_WITH_MSG((x)==NULL, #x<<" must be NULL, "<<ZVAR(x))
#define ZCHECK_NOT_NULL(x) ZCHECK_WITH_MSG((x)!=NULL, #x<<" must NOT be NULL, "<<ZVAR(x))
#define ZCHECK_ZERO(x) ZCHECK_WITH_MSG((x)==0, #x<<" must be 0, "<<ZVAR(x))
#define ZCHECK_NOT_ZERO(x) ZCHECK_WITH_MSG((x)!=0, #x<<" must NOT be 0, "<<ZVAR(x))

#define ZDCHECK_NULL(x) ZDCHECK_WITH_MSG((x)==NULL, #x<<" must be NULL, "<<ZVAR(x))
#define ZDCHECK_NOT_NULL(x) ZDCHECK_WITH_MSG((x)!=NULL, #x<<" must NOT be NULL, "<<ZVAR(x))
#define ZDCHECK_ZERO(x) ZDCHECK_WITH_MSG((x)==0, #x<<" must be 0, "<<ZVAR(x))
#define ZDCHECK_NOT_ZERO(x) ZDCHECK_WITH_MSG((x)!=0, #x<<" must NOT be 0, "<<ZVAR(x))

#define ZRCHECK_NULL(x) ZRCHECK_WITH_MSG((x)==NULL, #x<<" must be NULL, "<<ZVAR(x))
#define ZRCHECK_NOT_NULL(x) ZRCHECK_WITH_MSG((x)!=NULL, #x<<" must NOT be NULL, "<<ZVAR(x))
#define ZRCHECK_ZERO(x) ZRCHECK_WITH_MSG((x)==0, #x<<" must be 0, "<<ZVAR(x))
#define ZRCHECK_NOT_ZERO(x) ZRCHECK_WITH_MSG((x)!=0, #x<<" must NOT be 0, "<<ZVAR(x))


#define ZLOGI ZLOG(ZINFO)
#define ZLOGD ZLOG(ZDEBUG)
#define ZLOGV ZLOG(ZVERBOSE)
#define ZLOGM ZLOG(ZMORE)
#define ZLOGE ZLLOG(ZERROR)
#define ZLOGF ZLLOG(ZFATAL)

#define ZDLOGI ZDLOG(ZINFO)
#define ZDLOGD ZDLOG(ZDEBUG)
#define ZDLOGV ZDLOG(ZVERBOSE)
#define ZDLOGM ZDLOG(ZMORE)
#define ZDLOGE ZLDLOG(ZERROR)
#define ZDLOGF ZLDLOG(ZFATAL)

#define ZRLOGI ZRLOG(ZINFO)
#define ZRLOGD ZDLOG(ZDEBUG)
#define ZRLOGV ZRLOG(ZVERBOSE)
#define ZRLOGM ZRLOG(ZMORE)
#define ZRLOGE ZLRLOG(ZERROR)
#define ZRLOGF ZLRLOG(ZFATAL)

#define ZMSG (ZLOGI)

////////////////////////////////////////////////
string SplitLine(const string &msg, int linelen=60);
string SplitLine3(const string &msg, int linelen=60);
class ScopeMessage {
public:
  ScopeMessage(const string &msg) {ZLOGI << msg << " ... ";}
  ~ScopeMessage() {ZLOGI << "Done.\n";}
};

} //  namespace zzz
